﻿/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.

Copyright (C) 2005-2014  Sylvain Rougeaux (Cyber Sinh) (http://www.luminescence-software.org/)
*/

using System;
using System.IO;
using System.Text;
using System.Windows.Media.Imaging;

namespace Luminescence.Xiph
{
   /// <summary>
   ///   Classe de lecture / écriture des images dans les fichiers FLAC.
   /// </summary>
   public class ID3PictureFrame
   {
      /// <summary>
      ///   Constructeur de la classe ID3Picture.
      /// </summary>
      /// <param name = "data">Données METADATA_BLOCK_PICTURE sous la forme d'un tableau d'octets</param>
      public ID3PictureFrame(byte[] data)
      {
         using (var ms = new MemoryStream(data, 0, data.Length, false))
         {
            var br = new BinaryReader(ms);

            PictureType = (ID3PictureType)br.ReadBigEndianInt32();

            int mimeSize = br.ReadBigEndianInt32();
            ms.Position += mimeSize;

            int descSize = br.ReadBigEndianInt32();
            if (descSize > 0)
            {
               byte[] buffer = br.ReadBytes(descSize);
               var utf8 = new UTF8Encoding();
               Description = utf8.GetString(buffer);
            }

            ms.Position += 16;
            int pictSize = br.ReadBigEndianInt32();
            PictureData = br.ReadBytes(pictSize);
         }
      }

      /// <summary>
      ///   Tableau d'octets représentant les données brutes de l'image.
      /// </summary>
      public byte[] PictureData { get; }

      /// <summary>
      ///   Constructeur de la classe ID3Picture.
      /// </summary>
      /// <param name = "bitmap">Bitmap constituant l'image</param>
      /// <param name = "pictType">Type d'image selon la norme ID3v2 (frame APIC)</param>
      public ID3PictureFrame(BitmapFrame bitmap, ID3PictureType pictType)
      {
         using (var image = new MemoryStream())
         {
            BitmapEncoder be = BitmapEncoder.Create(bitmap.Decoder.CodecInfo.ContainerFormat);
            be.Frames.Add(bitmap);
            be.Save(image);

            PictureData = image.ToArray();
         }

         PictureType = pictType;
      }

      /// <summary>
      ///   Constructeur de la classe ID3Picture.
      /// </summary>
      /// <param name = "image">Données constituant l'image</param>
      /// <param name = "pictType">Type d'image selon la norme ID3v2 (frame APIC)</param>
      public ID3PictureFrame(byte[] image, ID3PictureType pictType)
      {
         PictureData = image;
         PictureType = pictType;
      }

      /// <summary>
      ///   Type d'image selon la norme ID3v2 (frame APIC).
      /// </summary>
      public ID3PictureType PictureType { get; set; }

      /// <summary>
      ///   Description de l'image.
      /// </summary>
      public string Description { get; set; }

      /// <summary>
      ///   Bitmap constituant l'image.
      /// </summary>
      public BitmapFrame GetBitmapFrame()
      {
         using (var ms = new MemoryStream(PictureData, 0, PictureData.Length))
            return BitmapFrame.Create(ms, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None);
      }

      /// <summary>
      ///   Type MIME de l'image passée en paramètre.
      /// </summary>
      public static string GetMimeType(BitmapFrame bitmap)
      {
         string mimes = bitmap.Decoder.CodecInfo.MimeTypes;
         int index = mimes.IndexOf(',');
         return index == -1 ? mimes : mimes.Substring(0, index);
      }

      /// <summary>
      ///   Retourne les données de l'image à la norme ID3 (frame APIC).
      /// </summary>
      /// <returns>Données de la frame sous la forme d'un tableau d'octets</returns>
      public byte[] ToApicFrame()
      {
         BitmapFrame bitmap = GetBitmapFrame();
         using (var ms = new MemoryStream())
         {
            byte[] buffer = ((int)PictureType).ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);

            var ascii = new ASCIIEncoding();
            byte[] binMime = ascii.GetBytes(GetMimeType(bitmap));
            buffer = binMime.Length.ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);
            ms.Write(binMime, 0, binMime.Length);

            if (String.IsNullOrEmpty(Description))
            {
               ms.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);
            }
            else
            {
               var utf8 = new UTF8Encoding();
               byte[] binDesc = utf8.GetBytes(Description);
               buffer = binDesc.Length.ToBigEndian();
               ms.Write(buffer, 0, buffer.Length);
               ms.Write(binMime, 0, binMime.Length);
            }

            buffer = bitmap.PixelWidth.ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);

            buffer = bitmap.PixelHeight.ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);

            int bitsPerPixel = bitmap.Format.BitsPerPixel;
            int nbIndexedColors = 0;
            if (bitmap.Palette != null)
               nbIndexedColors = bitmap.Palette.Colors.Count;

            buffer = bitsPerPixel.ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);

            buffer = nbIndexedColors.ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);

            buffer = PictureData.Length.ToBigEndian();
            ms.Write(buffer, 0, buffer.Length);
            ms.Write(PictureData, 0, PictureData.Length);

            return ms.ToArray();
         }
      }
   }
}